package com.shao;

import com.shao.compress.Compressor;
import com.shao.compress.impl.GzipCompress;
import com.shao.discovery.RegistryConfig;
import com.shao.loadBalance.LoadBalancer;
import com.shao.loadBalance.impl.RoundRobinLoadBalancer;
import com.shao.serialize.Serializer;
import com.shao.serialize.impl.JdkSerialize;
import lombok.Data;
import org.apache.zookeeper.common.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;

/**
 * 全局配置类
 * 代码配置-->xml配置-->默认配置
 * @author PaoFu
 * @version 1.0
 * @data 2025/5/15 16:57
 */
@Data
public class Configuration {

    private static final Logger log = LoggerFactory.getLogger(Configuration.class);
    // 端口
    private int port = 8092;
    // 序列化类型
    private String serializeType = "jdk";
    // 压缩类型
    private String compressType = "gzip";
    // 负载均衡类型
    private String loadBalancerType = "hessianLoadBalancer";
    // 应用名
    private String appName = "default";
    // 集群地址
    private String registryUrl = "zookeeper://127.0.0.1:2181";

    // 注册中心配置
    private RegistryConfig registryConfig = new RegistryConfig(registryUrl);
    // 序列化协议配置
    private ProtocolConfig protocolConfig = new ProtocolConfig(serializeType);
    // 序列化器
    private Serializer serializer = new JdkSerialize();
    // 负载均衡器
    private LoadBalancer loadBalancer = new RoundRobinLoadBalancer();
    // 压缩器
    private Compressor compressor = new GzipCompress();
    // ID生成器
    private IdGenerator idGenerator = new IdGenerator(1,2);


    public Configuration() {
        // 读取xml配置
        loadFromXml(this);
    }

    private void loadFromXml(Configuration configuration) {
        String xmlUri = "srpc.xml";
        Document document = getDocument(xmlUri);
        if (document == null) return;
        XPathFactory xPathFactory = XPathFactory.newInstance();
        XPath xPath = xPathFactory.newXPath();
        // 表达式
        String portExpression = "/configuration/port";
        String port = parseXmlElement2String(document, xPath, portExpression);
        if (!StringUtils.isBlank(port)) {
            configuration.port = Integer.parseInt(port);
        }
        String appNameExpression = "/configuration/appName";
        String appName = parseXmlElement2String(document, xPath, appNameExpression);
        if (!StringUtils.isBlank(appName)) {
            configuration.appName = appName;
        }
        String registryExpression = "/configuration/registry";
        String registryUrl = parseXmlElement2String(document, xPath, registryExpression);
        if (!StringUtils.isBlank(registryUrl)) {
            configuration.setRegistryUrl(registryUrl);
            configuration.setRegistryConfig(new RegistryConfig(registryUrl));
        }


        // Type
        String serializeTypeExpression = "/configuration/serializeType";
        String serializeType = parseXmlElement2String(document, xPath, serializeTypeExpression, "type");
        if (!StringUtils.isBlank(serializeType)) {
            configuration.setSerializeType(serializeType);
            configuration.setProtocolConfig(new ProtocolConfig(serializeType));
        }
        String compressTypeExpression = "/configuration/compressType";
        String compressType = parseXmlElement2String(document, xPath, compressTypeExpression, "type");
        if (!StringUtils.isBlank(compressType)) {
            configuration.setCompressType(compressType);
        }
        String loadBalanceTypeExpression = "/configuration/loadBalanceType";
        String loadBalanceType = parseXmlElement2String(document, xPath, loadBalanceTypeExpression, "type");
        if (!StringUtils.isBlank(loadBalanceType)) {
            configuration.setLoadBalancerType(loadBalanceType);
        }


        String serializerExpression = "/configuration/serializer";
        Serializer serializer = parseXmlElement2Object(document, xPath, serializerExpression, "class", null, (Object) null);
        if (serializer != null) configuration.setSerializer(serializer);
        String compressorExpression = "/configuration/compressor";
        Compressor compressor = parseXmlElement2Object(document, xPath, compressorExpression, "class", null, (Object) null);
        if (compressor != null) configuration.setCompressor(compressor);
        String loadBalancerExpression = "/configuration/loadBalancer";
        LoadBalancer loadBalancer = parseXmlElement2Object(document, xPath, loadBalancerExpression, "class", null, (Object) null);
        if (loadBalancer != null) configuration.setLoadBalancer(loadBalancer);
        IdGenerator idGenerator = resolveIdGenerator(document, xPath);
        if (idGenerator != null) configuration.setIdGenerator(idGenerator);
    }

    private IdGenerator resolveIdGenerator(Document document, XPath xPath) {
        String idGeneratorExpression = "/configuration/idGenerator";
        String idGeneratorClass = parseXmlElement2String(document, xPath, idGeneratorExpression, "class");
        long dataCenterId = Long.parseLong(Objects.requireNonNull(parseXmlElement2String(document, xPath, idGeneratorExpression, "dataCenterId")));
        long machineId = Long.parseLong(Objects.requireNonNull(parseXmlElement2String(document, xPath, idGeneratorExpression, "machineId")));

        try {
            Class<?> aClass = Class.forName(idGeneratorClass);
            return (IdGenerator) aClass.getConstructor(new Class[]{long.class, long.class}).newInstance(machineId, dataCenterId);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException |
                 ClassNotFoundException e) {
            log.error("解析xml配置Id生成器时发生异常", e);
        }
        return null;
    }

    /**
     * 解析xml元素为对象
     * @param document 文档对象
     * @param xPath xPath解析器
     * @param expression xPath表达式
     * @param attrName 提取的属性名
     * @param paramsType 参数类型
     * @param params 参数值
     * @return 返回解析对象
     * @param <T> T
     */
    private <T> T parseXmlElement2Object(Document document, XPath xPath, String expression,
                                         String attrName, Class<?>[] paramsType, Object... params) {
        try {
            XPathExpression xPathExpression = xPath.compile(expression);
            Node targetNode = (Node) xPathExpression.evaluate(document, XPathConstants.NODE);
            Node node = targetNode.getAttributes().getNamedItem(attrName);
            String classname = node.getNodeValue();
            Class<?> aClass = Class.forName(classname);
            return (T)((paramsType == null || paramsType.length == 0)
                    ? aClass.getConstructor().newInstance()
                    : aClass.getConstructor(paramsType).newInstance(params));
        } catch (XPathExpressionException | ClassNotFoundException | InstantiationException | IllegalAccessException |
                 NoSuchMethodException | InvocationTargetException e) {
            log.error("使用【{}】表达式解析xml标签对象的时候发生异常", expression, e);
        }
        return null;
    }

    /**
     * 解析xml元素的属性的值   <port value="9999"></port>
     * @param document 文档对象
     * @param xPath xPath解析器
     * @param expression xPath表达式
     * @param attrName 提取的属性名
     * @return 返回解析的属性的值
     */
    private String parseXmlElement2String(Document document, XPath xPath, String expression, String attrName) {
        try {
            XPathExpression xPathExpression = xPath.compile(expression);
            Node targetNode = (Node) xPathExpression.evaluate(document, XPathConstants.NODE);
            return targetNode.getAttributes().getNamedItem(attrName).getNodeValue();
        } catch (XPathExpressionException e) {
            log.error("使用【{}】表达式解析xml标签属性的值的时候发生异常", expression);
        }
        return null;
    }

    /**
     * 解析xml标签的文本值      <port>9999</port>
     * @param doc 文档对象
     * @param xPath xPath解析器
     * @param expression xPath表达式
     * @return 返回标签文本
     */
    private String parseXmlElement2String(Document doc, XPath xPath, String expression) {
        try {
            XPathExpression xPathExpression = xPath.compile(expression);
            Node targetNode = (Node) xPathExpression.evaluate(doc, XPathConstants.NODE);
            if (targetNode != null) {
                return targetNode.getTextContent();
            }
        } catch (XPathExpressionException e) {
            log.error("使用【{}】表达式解析xml标签文本值的时候发生异常", expression, e);
        }
        return null;
    }

    private static Document getDocument(String xmlUri) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(false);
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
            DocumentBuilder documentBuilder = factory.newDocumentBuilder();
            InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(xmlUri);
            return documentBuilder.parse(in);
        } catch (ParserConfigurationException | SAXException | IOException e) {
            log.error("解析xml任务获取document时发生异常", e);
        }
        return null;
    }
}
